interface Options {
  el: string | HTMLElement;
}

interface VueCls {
  options: Options;
  init(): void;
}

interface VNode {
  tag: string;
  text?: string;
  children?: VNode[];
}

class VDom {
  createElement(el: string) {
    return document.createElement(el);
  }
  setElementText(el: HTMLElement, text: string) {
    el.textContent = text;
  }
  render(vnode: VNode) {
    const root = this.createElement(vnode.tag);
    if (vnode.children && Array.isArray(vnode.children)) {
      vnode.children.forEach((child) => {
        root.appendChild(this.render(child));
      });
    } else {
      this.setElementText(root, vnode.text || '');
    }
    return root;
  }
}

class Vue extends VDom implements VueCls {
  options: Options;
  constructor(options: Options) {
    super();
    this.options = options;
    this.init();
  }
  init(): void {
    const data: VNode = {
      tag: 'div',
      children: [
        {
          tag: 'p',
          text: 'hello vue',
        },
        {
          tag: 'p',
          text: 'hello vue2',
        },
        {
          tag: 'p',
          text: 'hello vue3',
        },
      ],
    };
    const app = this.render(data);
    typeof this.options.el === 'string'
      ? document.querySelector(this.options.el)?.appendChild(app)
      : this.options.el.appendChild(app);
  }
}

new Vue({
  el: '#app',
});
